#include "stdafx.h"
#include "crPathTraits.h"

NS_CR_BEGIN

#ifndef CR_FILESYSTEM_CODECVT_BUF_SIZE
# define CR_FILESYSTEM_CODECVT_BUF_SIZE 256
#endif

namespace filesystem
{
	const std::size_t default_codecvt_buf_size = CR_FILESYSTEM_CODECVT_BUF_SIZE;

	void convert_aux(
		const char* from,
		const char* from_end,
		wchar_t* to, wchar_t* to_end,
		std::wstring & target,
		const path_traits::codecvt_type & cvt)
	{
		std::mbstate_t state = std::mbstate_t();
		const char* from_next;
		wchar_t* to_next;

		std::codecvt_base::result res;

		if ((res=cvt.in(state, from, from_end, from_next, to, to_end, to_next)) != std::codecvt_base::ok)
		{
			throw(std::exception("filesystem::path codecvt to wstring"));
		}
		target.append(to, to_next);
	}

	void convert_aux(
		const wchar_t* from,
		const wchar_t* from_end,
		char* to, char* to_end,
		std::string & target,
		const path_traits::codecvt_type & cvt)
	{
		std::mbstate_t state = std::mbstate_t();  // perhaps unneeded, but cuts bug reports
		const wchar_t* from_next;
		char* to_next;

		std::codecvt_base::result res;

		if ((res = cvt.out(state, from, from_end, from_next, to, to_end, to_next)) != std::codecvt_base::ok)
		{
			throw(std::exception("filesystem::path codecvt to wstring"));
		}
		target.append(to, to_next);
	}

	namespace path_traits
	{
		void convert(const char* from, const char* from_end, std::wstring & to, const codecvt_type& cvt)
		{
			CR_ASSERT(from);

			if (!from_end)
			{
				from_end = from + _STD strlen(from);
			}

			if (from == from_end)
			{
				return;
			}

			_STD size_t buf_size = (from_end - from) * 3;

			if (buf_size > default_codecvt_buf_size)
			{
				scoped_array< wchar_t > buf(new wchar_t[buf_size]);
				convert_aux(from, from_end, buf.get(), buf.get() + buf_size, to, cvt);
			}
			else
			{
				wchar_t buf[default_codecvt_buf_size];
				convert_aux(from, from_end, buf, buf + default_codecvt_buf_size, to, cvt);
			}
		}

		void convert(const wchar_t* from, const wchar_t* from_end, std::string & to, const codecvt_type& cvt)
		{
			CR_ASSERT(from);

			if (!from_end)  // null terminated
			{
				from_end = from + std::wcslen(from);
			}

			if (from == from_end) return;

			std::size_t buf_size = (from_end - from) * 4;
			buf_size += 4;

			if (buf_size > default_codecvt_buf_size)
			{
				scoped_array< char > buf(new char[buf_size]);
				convert_aux(from, from_end, buf.get(), buf.get() + buf_size, to, cvt);
			}
			else
			{
				char buf[default_codecvt_buf_size];
				convert_aux(from, from_end, buf, buf + default_codecvt_buf_size, to, cvt);
			}
		}
	}
}
NS_CR_END